'''
Description: 
Author: Zilu
Date: 2021-11-10 13:33:02
Version: 
LastEditTime: 2021-11-10 14:47:11
LastEditors: Zilu
'''

# UNIX/Linux/Mac 上可以用os.fork()创建子进程
# Windows 用multiprocessing模块的Process创建多进程

from multiprocessing import Pool, Process
import os

# 子进程执行的代码
# getppid() 获取父进程 id，getpid() 获取当前进程 id
def run_proc(name):
    print('Run child process %s (%s)...' % (name, os.getpid()))

# if __name__ == '__main__':
#     print('Parent process %s.' % os.getpid())
#     # args 参数使用 tuple
#     p = Process(target=run_proc, args=('test',))
#     print('Child process will start.')
#     p.start()
#     p.join()    # 进程间同步
#     print('Child process end.')

# 启用大量子进程，使用进程池 pool
import time, random
def longTimeTask(name):
    print('Run task %s (%s)...' % (name, os.getpid()))
    start = time.time()
    time.sleep(random.random() * 3)
    end = time.time()
    print('Task %s runs %0.2f seconds.' % (name, end - start))

if __name__ == '__main__':
    print("Parent process %s." % os.getpid())
    # Pool 的大小是最多可以同时运行的进程数，默认是 CPU的核数，
    p = Pool(4)
    for i in range(5):
        p.apply_async(longTimeTask, args=(i,))
    print("Waiting for all subprocesses done...")
    p.close()
    p.join()
    print("All subprocess done.")

# 使用subprocess模块可以很方便地启动一个子进程，并且控制其输入和输出
import subprocess

print('$ nslookup www.python.org')
r = subprocess.call(['nslookup','www.python.org'])
print('Exit code:', r)

# 可以使用communicate给子进程输入
print('$ nslookup')
p = subprocess.Popen(['nslookup'],stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate(b'set q=mx\npython.org\nexit\n')
print(output.decode('gbk'))
print("Exit code:", p.returncode)

# 进程间可以使用Queue、Pipe来通信
from multiprocessing import Process, Queue
import os, random, time

# 写数据进程执行的代码
def write(q:Queue):
    print('Process to write: %s' % os.getpid())
    for value in ['A', 'B', 'C']:
        print('Put %s in value.' % value)
        q.put(value)
        time.sleep(random.random())

def read(q:Queue):
    print('Process to read: %s' % os.getpid())
    while True:
        # True 返回一个块的数据
        value = q.get(True)
        print('Get %s from queue.' % value)

if __name__ == '__main__':
    q = Queue()
    pw = Process(target=write,args=(q,))
    pr = Process(target=read, args=(q,))
    pw.start()
    pr.start()
    pw.join()
    # pr进程里是死循环，无法等待其结束，只能强行终止:
    pr.terminate()


# 由于Windows没有fork调用，因此，multiprocessing需要“模拟”出fork的效果，
# 父进程所有Python对象都必须通过pickle序列化再传到子进程去，
# 所以，如果multiprocessing在Windows下调用失败了，要先考虑是不是pickle失败了。


